From 3efd42ac2becd0c30c4050d47424245239ab1153 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sun, 1 Jun 2025 17:00:01 -0400 Subject: [PATCH] tests/libtest: Just use python as a webserver if no libsoup We only have a very few tests that actually need what we have in ostree-trivial-httpd that supports things like serving random 500 errors etc. If we don't have libsoup, then just use a python webserver. Signed-off-by: Colin Walters --- Makefile-tests.am | 8 ---- tests/libtest.sh | 46 +++++++++++++++++------ tests/test-pull-basicauth.sh | 1 + tests/test-pull-repeated.sh | 2 + tests/test-pull-resume.sh | 1 + tests/test-remote-headers.sh | 5 ++- tests/webserver.py | 73 ++++++++++++++++++++++++++++++++++++ 7 files changed, 115 insertions(+), 21 deletions(-) create mode 100755 tests/webserver.py diff --git a/Makefile-tests.am b/Makefile-tests.am index 475080ed..14b5fee3 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -473,14 +473,6 @@ dist_test_scripts += $(_installed_or_uninstalled_test_scripts) test_programs += $(_installed_or_uninstalled_test_programs) endif -if !USE_LIBSOUP_OR_LIBSOUP3 -no-soup-for-you-warning: - @echo "WARNING: $(PACKAGE) was built without libsoup, which is currently" 1>&2 - @echo "WARNING: required for many unit tests." 1>&2 - sleep 10 -check: no-soup-for-you-warning -endif - # Unfortunately the glib test data APIs don't actually handle # non-recursive Automake, so we change our code to canonically look # for tests/ which is just a symlink when installed. diff --git a/tests/libtest.sh b/tests/libtest.sh index e1eb1377..52fa946d 100755 --- a/tests/libtest.sh +++ b/tests/libtest.sh @@ -285,6 +285,26 @@ ostree_repo_init() { fi } +run_webserver() { + echo httpd=${OSTREE_HTTPD} + if test -z "${OSTREE_HTTPD:-}"; then + if test "$#" -gt 0; then + echo "fatal: unhandled arguments for webserver: $@" 1>&2 + exit 1 + fi + # Note this automatically daemonizes; close stdin to ensure it doesn't leak to the child. + ${test_srcdir}/webserver.py ${test_tmpdir}/httpd-port /dev/null + echo "Waiting for webserver..." + while test '!' -f ${test_tmpdir}/httpd-port; do + sleep 0.5 + done + else + ${OSTREE_HTTPD} --autoexit --log-file $(pwd)/httpd.log --daemonize -p ${test_tmpdir}/httpd-port "$@" + fi + port=$(cat ${test_tmpdir}/httpd-port) + echo "http://127.0.0.1:${port}" > ${test_tmpdir}/httpd-address +} + # The original one; use setup_fake_remote_repo2 for newer code, # down the line we'll try to port tests. setup_fake_remote_repo1() { @@ -316,9 +336,7 @@ setup_fake_remote_repo1() { mkdir ${test_tmpdir}/httpd cd httpd ln -s ${test_tmpdir}/ostree-srv ostree - ${OSTREE_HTTPD} --autoexit --log-file $(pwd)/httpd.log --daemonize -p ${test_tmpdir}/httpd-port "$@" - port=$(cat ${test_tmpdir}/httpd-port) - echo "http://127.0.0.1:${port}" > ${test_tmpdir}/httpd-address + run_webserver "$@" cd ${oldpwd} export OSTREE="${CMD_PREFIX} ostree --repo=repo" @@ -361,10 +379,8 @@ setup_fake_remote_repo2() { mkdir ${test_tmpdir}/httpd cd httpd ln -s ${test_tmpdir}/ostree-srv ostree - ${OSTREE_HTTPD} --autoexit --log-file $(pwd)/httpd.log --daemonize -p ${test_tmpdir}/httpd-port $args - port=$(cat ${test_tmpdir}/httpd-port) - echo "http://127.0.0.1:${port}" > ${test_tmpdir}/httpd-address - cd ${oldpwd} + run_webserver + cd ${oldpwd} export OSTREE="${CMD_PREFIX} ostree --repo=repo" } @@ -411,7 +427,11 @@ setup_os_repository () { shift bootmode=$1 shift - bootdir=${1:-usr/lib/modules/3.6.0} + bootdir=usr/lib/modules/3.6.0 + if test "$#" -gt 0; then + bootdir=$1 + shift + fi oldpwd=`pwd` @@ -527,9 +547,7 @@ EOF mkdir ${test_tmpdir}/httpd cd httpd ln -s ${test_tmpdir} ostree - ${OSTREE_HTTPD} --autoexit --daemonize -p ${test_tmpdir}/httpd-port - port=$(cat ${test_tmpdir}/httpd-port) - echo "http://127.0.0.1:${port}" > ${test_tmpdir}/httpd-address + run_webserver "$@" cd ${oldpwd} } @@ -614,6 +632,12 @@ skip_one_without_user_xattrs () { fi } +skip_without_ostree_httpd () { + if test -z "${OSTREE_HTTPD:-}"; then + skip "this test requires libsoup (ostree-trivial-httpd)" + fi +} + skip_without_user_xattrs () { if ! have_user_xattrs; then skip "this test requires xattr support" diff --git a/tests/test-pull-basicauth.sh b/tests/test-pull-basicauth.sh index 68287355..bef916af 100755 --- a/tests/test-pull-basicauth.sh +++ b/tests/test-pull-basicauth.sh @@ -19,6 +19,7 @@ set -euo pipefail . $(dirname $0)/libtest.sh +skip_without_ostree_httpd setup_fake_remote_repo1 "archive" "" "--require-basic-auth" echo '1..3' diff --git a/tests/test-pull-repeated.sh b/tests/test-pull-repeated.sh index b3e00742..51113b66 100755 --- a/tests/test-pull-repeated.sh +++ b/tests/test-pull-repeated.sh @@ -21,6 +21,8 @@ set -euo pipefail . $(dirname $0)/libtest.sh +skip_without_ostree_httpd + COMMIT_SIGN="" if has_ostree_feature gpgme; then COMMIT_SIGN="--gpg-homedir=${TEST_GPG_KEYHOME} --gpg-sign=${TEST_GPG_KEYID_1}" diff --git a/tests/test-pull-resume.sh b/tests/test-pull-resume.sh index d9185787..ec8a3bf4 100755 --- a/tests/test-pull-resume.sh +++ b/tests/test-pull-resume.sh @@ -21,6 +21,7 @@ set -euo pipefail . $(dirname $0)/libtest.sh +skip_without_ostree_httpd setup_fake_remote_repo1 "archive" "" "--force-range-requests" echo '1..1' diff --git a/tests/test-remote-headers.sh b/tests/test-remote-headers.sh index d3bf4f97..9648ef8c 100755 --- a/tests/test-remote-headers.sh +++ b/tests/test-remote-headers.sh @@ -19,10 +19,11 @@ set -euo pipefail -echo '1..2' - . $(dirname $0)/libtest.sh +skip_without_ostree_httpd +echo '1..2' + V=$($CMD_PREFIX ostree --version | \ python3 -c 'import sys, yaml; print(yaml.safe_load(sys.stdin)["libostree"]["Version"])') diff --git a/tests/webserver.py b/tests/webserver.py new file mode 100755 index 00000000..95a083ee --- /dev/null +++ b/tests/webserver.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# Run a webserver on a random port, serving the current working directory. +# The allocated port is written to the provided path +import http.server +from http.server import SimpleHTTPRequestHandler, ThreadingHTTPServer +import socket +import sys +import os +import argparse +import threading +import time +import contextlib + +def _get_best_family(*address): + infos = socket.getaddrinfo( + *address, + type=socket.SOCK_STREAM, + flags=socket.AI_PASSIVE, + ) + family, ty, proto, canonname, sockaddr = next(iter(infos)) + return family, sockaddr + +def run(port_path, HandlerClass=SimpleHTTPRequestHandler, + ServerClass=ThreadingHTTPServer, + protocol="HTTP/1.1", port=0, bind=None): + ServerClass.address_family, addr = _get_best_family(bind, port) + HandlerClass.protocol_version = protocol + + server = ThreadingHTTPServer(addr, HandlerClass) + + with server as httpd: + host, port = httpd.socket.getsockname()[:2] + with open(port_path + '.tmp', 'w') as f: + f.write(f'{port}\n') + os.rename(port_path + '.tmp', port_path) + print(f"Running on port {port}") + try: + httpd.serve_forever() + except KeyboardInterrupt: + print("\nKeyboard interrupt received, exiting.") + sys.exit(0) + +def main(): + # We automatically daemonize + pid = os.fork() + if pid > 0: + sys.exit(0) + os.setsid() + pid = os.fork() + if pid > 0: + sys.exit(0) + # code to spawn a thread and detect when the current working directory no longer exists, then exit + def watch_cwd(original_cwd): + while True: + try: + os.stat(original_cwd) + except FileNotFoundError: + sys.exit(0) + time.sleep(1) + + original_cwd = os.getcwd() + watcher_thread = threading.Thread(target=watch_cwd, args=(original_cwd,)) + watcher_thread.daemon = True + watcher_thread.start() + + parser = argparse.ArgumentParser() + parser.add_argument('port_path', metavar='PATH', + help='Write port used to this path ') + args = parser.parse_args() + run(args.port_path) + +if __name__ == '__main__': + main() -- 2.30.2